﻿///////////////////////////////////////////////////////////////////////////////
//
// This file is part of the BassDrop project
//   https://code.google.com/p/bass-drop/
//
//
// Creator:	Gergely Krajcsovszki (krajcsovszkig@hotmail.com)
// Date:	2013-07-24 19:00
//
//
// Copyright © 2013-2014. Gergely Krajcsovszki
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
///////////////////////////////////////////////////////////////////////////////
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Windows;
using BassDrop.Core.Common;
using BassDrop.Core.Desktop;
using BassDrop.Core.Common.Localization;
using System.Globalization;
using System.Collections.ObjectModel;
using BassDrop.DesktopClient.Properties;
using BassDrop.DesktopClient.Utilities;
using BassDrop.Core.Common.Exceptions;
using System.Threading;
using BassDrop.Core.Desktop.Plugins;
using BassDrop.Core.Common.Services;
using System.Threading.Tasks;
using System.Reflection;

namespace BassDrop.DesktopClient
{
	/// <summary>
	/// Interaction logic for App.xaml
	/// </summary>
	public partial class App : Application
	{
		#region Static properties

		/// <summary>
		/// The currently running application instance
		/// </summary>
		public static App Instance
		{
			get;
			private set;
		}

		/// <summary>
		/// List of exceptions occured at startup
		/// </summary>
		public static List<Exception> StartupExceptions
		{
			get;
			private set;
		}
		#endregion Static properties

		private async void Application_Startup( object sender, StartupEventArgs e )
		{
			Exception uncaughtError = null;

			try
			{
				Application.Current.DispatcherUnhandledException += UnhandledExceptionTrapper;

				SplashScreen splashScreen = new SplashScreen( "splashscreen.png" );
				splashScreen.Show( false, false );

				// Create global variables
				StartupExceptions = new List<Exception>();
				Instance = this;

				// Create and init the controller
				Controller.Init(
					new Uri( AppDomain.CurrentDomain.BaseDirectory ),
					new Uri( Environment.CurrentDirectory, UriKind.Absolute ),
					new BassDrop.Core.Desktop.Data.IO.Desktop.FileHandler(),
					new BassDrop.Core.Desktop.Plugins.PluginManager(),
					new BassDrop.Core.Desktop.Services.CryptoProvider(),
					new BassDrop.DesktopClient.UI.Dialogs.WebBrowserDialog(),
					new BassDrop.DesktopClient.Services.UserInterface(),
					Settings.Default );

				Controller.Instance.ApplicationLanguageChanged += Instance_ApplicationLanguageChanged;

				// Set culture and load languages
				try
				{
					Language.ScrapeLanguagesFrom( AppDomain.CurrentDomain.BaseDirectory + "Languages", ServiceCenter.FileHandler );
					SetLanguage( Language.ForLanguage( CultureInfo.InstalledUICulture.TwoLetterISOLanguageName ) ); // this will be overridden when the settings load
				}
				catch ( Exception ex )
				{
					StartupExceptions.Add( ex );
				}

				// Load plugins
				try
				{
					await ((PluginManager) Controller.Instance.PluginManager).LoadPluginsFrom( AppDomain.CurrentDomain.BaseDirectory + "Plugins" );
				}
				catch ( Exception ex )
				{
					StartupExceptions.Add( ex );
				}

				// Apply settings
				try
				{
					Settings.Default.Apply();
				}
				catch ( Exception ex )
				{
					StartupExceptions.Add( ex );
				}

				// Finish controller initialization
				try
				{
					Controller.Instance.FinishInit();

					// Add additional options sets
					Controller.Instance.ProgramOptions.Add( new BassDrop.DesktopClient.Options.OptionsSets.PlaylistDisplayOptionsSet() );

					// Set defaults for the properties that weren't inited
					if ( Controller.Instance.ApplicationLanguage == null )
					{
						Controller.Instance.ApplicationLanguage = Language.ForLanguage( CultureInfo.InstalledUICulture.TwoLetterISOLanguageName );
					}
				}
				catch ( Exception ex )
				{
					StartupExceptions.Add( ex );
				}

				// Show main window
				Instance.MainWindow = new MainWindow( splashScreen );
				Instance.MainWindow.Show();
			}
			catch ( Exception ex )
			{
				uncaughtError = ex;
			}

			if ( uncaughtError != null )
			{
				await new UnhandledException( uncaughtError ).Prompt();
			}
		}

		/// <summary>
		/// Sets the language for the application and saves it in the settings.
		/// Loads the strings for the locale.
		/// 
		/// If the strings cannot be loaded, nothing is done.
		/// </summary>
		/// <param name="language">the selected locale</param>
		public void SetLanguage( Language language )
		{
			Strings.LoadStringsFor( language, ServiceCenter.FileHandler );

			CultureInfo culture = new CultureInfo( Strings.SelectedLanguage.LanguageCode );

			CultureInfo.DefaultThreadCurrentCulture = culture;
			CultureInfo.DefaultThreadCurrentUICulture = culture;
			Thread.CurrentThread.CurrentCulture = culture;
			Thread.CurrentThread.CurrentUICulture = culture;
		}

		/// <summary>
		/// Applies a skin to the application
		/// </summary>
		/// <param name="skinDictionaryUri">the URI of the skin to be applied</param>
		/// <remarks>
		/// From http://www.codeproject.com/Articles/19782/Creating-a-Skinned-User-Interface-in-WPF
		/// </remarks>
		public void ApplySkin( Uri skinDictionaryUri )
		{
			// Load the ResourceDictionary into memory.
			ResourceDictionary skinDict = Application.LoadComponent( skinDictionaryUri ) as ResourceDictionary;
			Collection<ResourceDictionary> mergedDicts = base.Resources.MergedDictionaries;

			// Check loaded resource
			string resourceKind = "";

			if ( skinDict.Contains( "ResourceDictionaryKind" ) )
			{
				resourceKind = (string) skinDict["ResourceDictionaryKind"];
			}

			if ( resourceKind != "UserSkins" )
			{
				throw new BadResourceException( Strings.ExceptionBadSkinFile );
			}

			// Remove the existing skin dictionaries
			for ( int i = mergedDicts.Count - 1; i >= 0; i-- )
			{
				if ( ((string) mergedDicts[i]["ResourceDictionaryKind"]) == "UserSkins" )
				{
					mergedDicts.RemoveAt( i );
				}
			}

			// Apply the selected skin so that all elements in the
			// application will honor the new look and feel.
			mergedDicts.Add( skinDict );
		}

		/// <summary>
		/// Closes the application properly
		/// </summary>
		/// <param name="force">force the application to close</param>
		/// <param name="exitCode">exit code for the application</param>
		public async Task Close( bool force = false, int exitCode = 0 )
		{
			bool readyToClose = false;
			Exception ex = null;

			try
			{
				Settings.Default.Save();
				await Controller.Destroy();

				readyToClose = true;
			}
			catch ( Exception e )
			{
				ex = e;
			}
			finally
			{
				if ( readyToClose || force )
				{
					Environment.Exit( exitCode );
				}
				else
				{
					throw new ShutdownFailedException( ex );
				}
			}
		}

		/// <summary>
		/// Handles unhandled exceptions by prompting the user about the problem then shutting down the application.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private async void UnhandledExceptionTrapper( object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e )
		{
			await new UnhandledException( e.Exception ).Prompt();
		}

		private void Instance_ApplicationLanguageChanged( object sender, System.ComponentModel.PropertyChangedEventArgs e )
		{
			SetLanguage( Controller.Instance.ApplicationLanguage );
		}
	}
}
